﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "StructureColumnEndTypeBehavior.h"
#include "AlgorithmBodyTopologyOperate.h"
#include "DbObjectUtils.h"
#include "GbmpBuiltInParameterDefinitions.h"
#include "GraphicsNodeUtils.h"
#include "IBody.h"
#include "IElementParameters.h"
#include "IElementPosition.h"
#include "IGraphicsBRepBody.h"
#include "IGraphicsElementShape.h"
#include "IParameter.h"
#include "IParameterDefinitionLibrary.h"
#include "IPlane.h"
#include "StructureInstanceEndType.h"
#include "StructureSlantedColumn.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

#define ColumnTopFaceIndex 1
#define ColumnBottomFaceIndex 0

using namespace gcmp;

namespace
{
    bool FitBodyToTargetedFace(IBody *pBody, int tractiveFaceIndex, InstanceEndType targetFaceType, const Vector3d &point, const Vector3d &otherPoint)
    {
        if (!pBody || targetFaceType == InstanceEndType::Horizontal)
        {
            return true;
        }

        OwnerPtr<IPlane> opTargetFace;
        Vector3d direction = otherPoint - point;
        if (targetFaceType == InstanceEndType::Vertical)
        {
            direction.SetZ(0);
            direction.Normalize();
            opTargetFace = IPlane::Create(point, direction);
        }
        else if (targetFaceType == InstanceEndType::PerpendicularToAxis)
        {
            direction.Normalize();
            opTargetFace = IPlane::Create(point, direction);
        }
        else
        {
            return false;
        }

        IFace *pTractiveFace = const_cast<IFace *>(pBody->GetFace(tractiveFaceIndex));
        bool result = AlgorithmBodyTopologyOperate::FitBodyToTargetPlaneAlongVAxisWithoutTopoChange(opTargetFace.get(), pBody, pTractiveFace);

        return result;
    }
}

DBOBJECT_DATA_DEFINE(StructureColumnEndTypeBehavior)
{
    m_pOwnerElement = nullptr;
}

gcmp::StructureColumnEndTypeBehavior::StructureColumnEndTypeBehavior(IInstance *pInstance)
{
    m_pOwnerElement = pInstance;
}

bool gcmp::StructureColumnEndTypeBehavior::SetOwnerElement(IElement *pOwnerElement)
{
    m_pOwnerElement = pOwnerElement;
    return true;
}

const StructureSlantedColumn *gcmp::StructureColumnEndTypeBehavior::GetSlantedColumnData() const
{
    const IInstance *pInstance = quick_cast<IInstance>(m_pOwnerElement);
    const StructureSlantedColumn *pData = StructureSlantedColumn::Get(pInstance);

    return pData;
}

IInstance *gcmp::StructureColumnEndTypeBehavior::GetOwnerElement() const
{
    IInstance *pInstance = quick_cast<IInstance>(m_pOwnerElement);

    return pInstance;
}

void gcmp::StructureColumnEndTypeBehavior::ReportInputDataIds(std::vector<RegenDataId>& oInputDatas) const
{
    const StructureSlantedColumn *pData = GetSlantedColumnData();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pData, L"斜柱数据为空",L"GDMPLab",L"2024-03-30");

    oInputDatas.push_back(pData->GetTopSectionTypeRdId());
    oInputDatas.push_back(pData->GetBottomSectionTypeRdId());
}

OwnerPtr<IGraphicsElementShape> gcmp::StructureColumnEndTypeBehavior::Customize(OwnerPtr<IGraphicsElementShape> opGRep) const
{
    const StructureSlantedColumn *pData = GetSlantedColumnData();
    IInstance *pInstance = GetOwnerElement();
    DBG_WARN_AND_RETURN_UNLESS(pData && pInstance, opGRep, L"斜柱数据为空",L"GDMPLab",L"2024-03-30");

    IDocument* pDoc = pInstance->GetDocument();
    DBG_WARN_AND_RETURN_UNLESS(pDoc, opGRep, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    // 族生成的形体端面默认是水平的
    /*if (pData->GetTopSectionType() == InstanceEndType::Horizontal
        && pData->GetBottomSectionType() == InstanceEndType::Horizontal)
    {
        return TransferOwnership(opGRep);
    }*/

    // 获取柱两个端点
    IElementPosition *posBehavior = pInstance->GetElementPosition();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(posBehavior != nullptr, L"无效posBehavior",L"GDMPLab",L"2024-03-30");
    const IElementParameters* pElementParameters = pInstance->GetElementParameters();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElementParameters != nullptr, L"无效pElementParameters",L"GDMPLab",L"2024-03-30");
    
    double height = 0;
    bool hasParameter = pElementParameters->GetParameterValueAsDouble(PARAMETER_UID(ColumnHeightBuiltInParameter), height);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(hasParameter, L"没有高度参数",L"GDMPLab",L"2024-03-30");

    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(posBehavior->GetControlPointCount() == 2, L"斜柱应该有两个控制点",L"GDMPLab",L"2024-03-30");
    Vector3d startPt = posBehavior->GetPointByIndex(0);
    Vector3d endPt = posBehavior->GetPointByIndex(1);
    endPt.SetZ(startPt.Z() + height);

    // 获取柱Body
    std::vector<const IGraphicsBRepBody*> bodies;
    std::vector<Matrix4d> transforms;
    GraphicsNodeUtils::CollectBRepBodies(pInstance->GetDocument(), opGRep.get(), &bodies, &transforms);
    DBG_WARN_AND_RETURN_UNLESS(bodies.size() == 1, opGRep, L"斜柱默认Body应该只有一个",L"GDMPLab",L"2024-03-30");
    OwnerPtr<IGraphicsBRepBody> opBody = TransferOwnershipCast<IGraphicsBRepBody>(bodies.front()->Clone());
    opBody->Transform(transforms.front());

    // 这里的旋转和斜柱有关，只是临时做法，需要PositionBehavior支持项目定位线和族环境Y轴的映射关系
    Matrix4d rotate;
    rotate.MakeRotate(startPt, Vector3d::UnitZ, Constants::MATH_PI_2);
    opBody->Transform(rotate);
    // 镜像相关的问题，以后直柱斜柱的族模板可能会分开，到时候这里的临时处理可以去掉
    bool isMirror = false;
    hasParameter = pElementParameters->GetParameterValueAsBool(PARAMETER_UID(InstanceMirrorSignBuiltInParameter), isMirror);
    if (hasParameter && isMirror)
    {
        Matrix4d rotate;
        rotate.MakeRotate(startPt, Vector3d::UnitZ, Constants::MATH_PI);
        opBody->Transform(rotate);
    }

    // 根据截面样式修改Body
    IBody *pBody = dynamic_cast<IBody *>(opBody->GetGeometryFW());
    FitBodyToTargetedFace(pBody, ColumnTopFaceIndex, pData->GetTopSectionType(), endPt, startPt);
    FitBodyToTargetedFace(pBody, ColumnBottomFaceIndex, pData->GetBottomSectionType(), startPt, endPt);

    // 构造新的GRep
    OwnerPtr<IGraphicsElementShape> opNewGRep = IGraphicsElementShape::Create(opGRep->GetRenderLayer());
    opNewGRep->AddChild(TransferOwnership(opBody));
    Box3d newGrepBox = GraphicsNodeUtils::CalculateGraphicsNodeBoundingBox(pDoc, opNewGRep.get());
    opNewGRep->SetBox(newGrepBox);
    return opNewGRep;
}
